/*
#####################################################################
####################	   T R I B U I T 		#####################
#####################################################################

Copyright (C) 2010  Christoph Schnack, Berlin (Germany)

This file is part of Tribuit.

    Tribuit is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Tribuit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Tribuit.  If not, see <http://www.gnu.org/licenses/>.


########################################################################
*/

package Tribuit;

import GuiTools.*;
import JukeBox.JukeBox;
import Mainframe.Mainframe;
import OtherDialogs.Loader;
import OtherDialogs.UTF8Table;
import OtherDialogs.Introduction;
import PictureDescription.PictureStorage;
import Statistics.Statistics;
import Storage.*;
import Settings.*;

import javax.swing.*;

import java.awt.Dimension;
import java.awt.Toolkit;
import java.beans.ExceptionListener;
import java.io.*;
import java.util.*;




/**
 * Tribuit is the main class of Tribuit. By start it checks if "vocs/" exists or can be created, otherwise Tribuit
 * won't start.\n
 * All vocabulary data is held by an instance of Storage class and loaded, when the program starts.
 * Its reference is MAINVOC, all changes on words' data are made here and its method save() writes its data into one file per
 * vocabulary under vocs/, if MAINVOC's autosave is activated, otherwise the program asks for saving when closed. 
 * Its autosave mode can be selected in the settings dialog provided by GuiTools (OptionFrame).
 * The main frame is referenced by MAINFRAME.
 * 
 * Set VERBOSE true to get information on console.
 * 
 * @author Christoph Schnack 
 */
public class Tribuit {
	/**
	 * System.out.println messages are activated when true.
	 */
	static boolean VERBOSE = true;
	/**
	 * The reference to the main frame.
	 */
    Mainframe MAINFRAME;
    /**
     * The reference to the Storage object
     */
    Storage MAINVOC;
    /**
     * The reference to the Settings object
     */
    public Settings SET;
    /**
     * The reference to Setting classe's instance SET's current Language instance
     */
    static Loader loader;
    Language LANG;
    public PictureStorage PICSTO;
    UTF8Table CHARTABLE;

    public static void main(String[] args){
    	if(args!=null){
    		if(args.length>0){
    			for(String arg : args){
    				if(arg.equals("-v")) VERBOSE = true;
    			}
    		}
    	}
    	Tribuit t = new Tribuit();
		t.init();
    }
    
    /**
     * Initializes saved data and GUI, sets a file lock on vocabulary data files and sets the LookAndFeel.
     * @see #initStorage()
     */
    public void init(){
    	if(VERBOSE)System.out.println("#########INITIALIZING TRIBUIT############");
    	String javaVersion = System.getProperty("java.version");
    	String[] javaSplit = javaVersion.split("\\.");
    	if(VERBOSE)System.out.println("# java version: "+javaVersion);
    	if(VERBOSE)System.out.println("# tribuit home: "+GuiTools.TRIBUITHOME);
    	if(Integer.parseInt(javaSplit[0])<1 || (Integer.parseInt(javaSplit[0])>1)?false:Integer.parseInt(javaSplit[1])<6){
    		JOptionPane.showMessageDialog(null, "<HTML>Cannot start Tribuit.<br>java version 1.6. (aka java 6) or higher is required!<br>Please update your java runtime environment.", "Error", JOptionPane.ERROR_MESSAGE);
    		System.exit(0);
    	}
    	if(!GuiTools.isAnotherInstanceRunning()){
    		loader = new Loader();
			if(initStorage()){
				if(initPictureStorage()){
					MAINVOC.getStatistics().updateStatistics(loader);
					GuiTools.lockVocFiles();
					try {
						UIManager.setLookAndFeel( SET.getLookAndFeel() );
					}
					catch(Exception e){if(SET.isVerbose())e.printStackTrace();}
			    	SwingUtilities.invokeLater(new InitGui());
				}
				else{
					JOptionPane.showMessageDialog(null, "<HTML>Cannot start Tribuit!<br><br>Error occured while loading picture storage!<br>Please set read and write permission for applications<br> for 'path to tribuit'/pictureMaps and start again.<br>If so and this message appears again<br>you could delete all files in the directory<br>mentioned and then start again.", "TribuitError", JOptionPane.ERROR_MESSAGE);
					System.exit(0);
				}
			}
			else{
				JOptionPane.showMessageDialog(null, "<HTML>Cannot start Tribuit.<br><br>Error occured while loading VOCABULARY storage!<br>Please set read and write permission for applications<br> for 'path to tribuit'/vocs and start again.<br>If so and this message appears again<br>you could delete all files in the directory<br>mentioned and then restart Tribuit.", "TribuitError", JOptionPane.ERROR_MESSAGE);
				System.exit(0);
			}
    	}
    	else{
    		JOptionPane.showMessageDialog(null,"Tribuit is already running!");
    		if(VERBOSE)System.out.println("#########EXIT TRIBUIT############");
    		System.exit(0);
    	}
    }
    
    /**
     * Builds MAINFRAME.
     */
    private class InitGui implements Runnable{
    	public void run(){
	        MAINFRAME = new Mainframe(MAINVOC,PICSTO,SET);
	        MAINVOC.setMainframe(MAINFRAME);
	        int x,y;
	        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
			if(screenSize.getHeight()<768){
				x=20;
				y=20;
			}
			else{
				x=150;
				y=100;
			}
	        MAINFRAME.showUp(x,y);
	        if(SET.getFirstStart())new Introduction(MAINFRAME,MAINVOC,SET);
	        MAINFRAME.luPanel.searchTF.requestFocus();
	    }
    }
    
    public boolean initPictureStorage(){
    	PICSTO=new PictureStorage(SET);
    	return PICSTO.isLoaded();
    }

    /**
     * Initializes Setting, reads or creates the vocabularies's storage path "vocs/" and loads its content.
     * @see #initGui()
     * @return true, if initializing/detecting was successful
     */
    public boolean initStorage(){
		MAINVOC = new Storage(MAINFRAME);
		SET = new Settings(MAINFRAME,MAINVOC,VERBOSE);
    	LANG = SET.getLanguage();
    	MAINVOC.setSettings(SET);
    	if(SET.isVerbose())System.out.println("\n#---INITIALZING WORD-STORAGE---");
		File PATH = new File(GuiTools.TRIBUITHOME+File.separator+"vocs");
		boolean status = PATH.isDirectory();
		if(status){
		    if(VERBOSE)System.out.println("#VOCABULARY PATH FOUND: "+PATH.getPath());
	    	return load();
		}
		else{
			if(VERBOSE)System.out.print("#WORD-STORAGE PATH NOT FOUND\n#CREATING PATH: '"+PATH.getPath() + "' ...");
	    	if(PATH.mkdir()){
				if(VERBOSE) System.out.println("#...SUCCESSFULLLY");
				return true;
	    	}
	    	else{
				if(VERBOSE) System.out.println("#FAILED!!!UNABLE TO WRITE STORAGE-FILE!!!");
				JOptionPane.showMessageDialog(null, LANG.getErrorNoRights());
				return false;
	    	}
		}
    }

  
    /**
     * 
     * Reads out "vocs/", filters out all files with suffix .lang and fills MAINVOC with it's eventual content.
     * 
     */

    public boolean load(){
    	boolean oldAutosave=SET.getAutosave();
    	SET.setAutosave(false);
		if(VERBOSE) System.out.println("#commencing scan for vocabularies");
		try{
			File dir = new File(GuiTools.TRIBUITHOME+File.separator+"vocs");
			String[] fileList = dir.list(new FilenameFilter() {
    		    public boolean accept(File d, String name) {
    		       return name.endsWith(".voc");
    		    }
			});
			if(VERBOSE) System.out.println("#Vocabularies: "+fileList.length);
			String wd;
			Vector<String> loaded = new Vector<String>();
			Vector<Word> wordBuffer = new Vector<Word>();
			int loaderCounter =0;
			loader.start();
			if(fileList.length>0){
				for(int r=0;r<fileList.length;r++){
					StringBuffer loads = new StringBuffer();
					File f = new File(GuiTools.TRIBUITHOME+File.separator+"vocs"+File.separator+fileList[r]);
					BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(f),"UTF-8"));
					String line = null;
					while((line = reader.readLine()) != null){
						loads.append(line);
					}
					reader.close();
					if(loads.length()>0){
						String[] voc_split = loads.toString().split("=");
						boolean newVersion = voc_split[0].equals("13");
						String tmpVocName = "";
						String[] tcsA,tcsB;
						if(newVersion){
							String[] voc_names = voc_split[1].split(":");
							MAINVOC.addVocabulary(MAINVOC.makeVocabulary(voc_names[0],voc_names[1],true));
							tmpVocName = voc_names[0]+" - "+voc_names[1];
							Vector<String> catsA = new Vector<String>(),catsB = new Vector<String>();
							for(String s : voc_split[2].split("<")){
								if(s!=null&&!catsA.contains(s))catsA.add(s);
							}
							for(String s : voc_split[3].split("<")){
								if(s!=null&&!catsB.contains(s))catsB.add(s);
							}
							MAINVOC.getVocabulary(tmpVocName).setCategoriesA(catsA);
							MAINVOC.getVocabulary(tmpVocName).setCategoriesB(catsB);
							Vector<String> stckA = new Vector<String>(),stckB = new Vector<String>();
							for(String s : voc_split[4].split("<")){
								if(s!=null&&!stckA.contains(s))stckA.add(s);
							}
							for(String s : voc_split[5].split("<")){
								if(s!=null&&!stckB.contains(s))stckB.add(s);
							}
							MAINVOC.getVocabulary(tmpVocName).setSortedTermClassKeysA(stckA);
							MAINVOC.getVocabulary(tmpVocName).setSortedTermClassKeysB(stckB);
							tcsA = voc_split[6].split(">");
							tcsB = voc_split[7].split(">");
						}
						else{
							String[] voc_names = voc_split[0].split(":");
							MAINVOC.addVocabulary(MAINVOC.makeVocabulary(voc_names[0],voc_names[1],true));
							tmpVocName = voc_names[0]+" - "+voc_names[1];
							tcsA = voc_split[1].split(">");
							tcsB = voc_split[2].split(">");
						}
						Vocabulary loadingVoc = MAINVOC.getVocabulary(tmpVocName);
						loadingVoc.deleteAllTermClasses();
						for(int t = 0;t<tcsA.length;t++){
							if(tcsA[t]!=null){
								String[] tcsAPreSplit = tcsA[t].split("#");
								String[] tcs = tcsAPreSplit[0].split(":");
								loadingVoc.TCsAPut(tcs[0],new TermClass());
								if(!tcs[1].equals("~~~")){
									String[] tVals =tcs[1].split(";");
									for(int tv=0;tv<tVals.length;tv++){
										String[] metas = tVals[tv].split("%");
										Vector<String> vector = null;
										if(!metas[1].equals("~")){
											vector = new Vector<String>();
											String[] vVals = metas[1].split(",");
											for(int vv=0;vv<vVals.length;vv++){
												if(vVals[vv]!=null)vector.add(vVals[vv]);
											}
										}
										loadingVoc.TCsAPutMeta(tcs[0], metas[0], vector);
									}
									String[] sk = tcs[2].split(",");
									Vector<String> skAdd = new Vector<String>(); 
									for(int skInd=0;skInd<sk.length;skInd++){
										if(sk[skInd]!=null)skAdd.add(sk[skInd]);
									}
									loadingVoc.TCsASetSortedKeys(tcs[0], skAdd);
								}
								if(!tcsAPreSplit[1].equals("~~~")){
									String[] pfA = tcsAPreSplit[1].split("%");
									Vector<String> arg=null;
									if(!pfA[1].equals("~")){
										arg = new Vector<String>();
										for(String s : pfA[1].split(",")){
											if(s!=null)arg.add(s);
										}
									}
									loadingVoc.TCsASetPreFix(tcs[0], pfA[0], arg);
								}
							}
						}
						
						for(int t = 0;t<tcsB.length;t++){
							if(tcsB[t]!=null){
								String[] tcsBPreSplit = tcsB[t].split("#");
								String[] tcs = tcsBPreSplit[0].split(":");
								loadingVoc.TCsBPut(tcs[0],new TermClass());
								if(!tcs[1].equals("~~~")){
									String[] tVals =tcs[1].split(";");
									for(int tv=0;tv<tVals.length;tv++){
										String[] metas = tVals[tv].split("%");
										Vector<String> vector = null;
										if(!metas[1].equals("~")){
											vector = new Vector<String>();
											String[] vVals = metas[1].split(",");
											for(int vv=0;vv<vVals.length;vv++){
												if(vVals[vv]!=null)vector.add(vVals[vv]);
											}
										}
										loadingVoc.TCsBPutMeta(tcs[0], metas[0], vector);
									}
									String[] sk = tcs[2].split(",");
									Vector<String> skAdd = new Vector<String>(); 
									for(int skInd=0;skInd<sk.length;skInd++){
										if(sk[skInd]!=null)skAdd.add(sk[skInd]);
									}
									loadingVoc.TCsBSetSortedKeys(tcs[0], skAdd);
								}
								if(!tcsBPreSplit[1].equals("~~~")){
									String[] pfB = tcsBPreSplit[1].split("%");
									Vector<String> arg=null;
									if(!pfB[1].equals("~")){
										arg = new Vector<String>();
										for(String s : pfB[1].split(",")){
											if(s!=null)arg.add(s);
										}
									}
									loadingVoc.TCsBSetPreFix(tcs[0], pfB[0], arg);
								}
							}
						}
						
						if(!newVersion){
							Vector<String> sortedTCKeysA = new Vector<String>(),sortedTCKeysB = new Vector<String>();
							for(String s : loadingVoc.getTermclassesKeysA()){
								sortedTCKeysA.add(s);
							}
							for(String s : loadingVoc.getTermclassesKeysB()){
								sortedTCKeysB.add(s);
							}
							loadingVoc.setSortedTermClassKeysA(sortedTCKeysA);
							loadingVoc.setSortedTermClassKeysB(sortedTCKeysB);
							if(!voc_split[3].equals("nowords")){
								String[] words_split = voc_split[3].split(";");
								if(VERBOSE) System.out.println("# "+tmpVocName+" has "+words_split.length+" words");
								Vector<Meaning> mngsB,mngsA;
								Meaning mA,mB;
								String termAString,termBString,article;
								for(int j=0;j<words_split.length;j++){
									mngsA = new Vector<Meaning>();
									mngsB = new Vector<Meaning>();
									mA = new Meaning();
									mB = new Meaning();
									String[] w = words_split[j].split(">");
									String lection = w[0];
									if(w[0].equals("~"))lection = LANG.getUnknown();
									String[] termA =w[1].split(":");
									article = termA[0].equals("~")?"":termA[0];
									mA.setArticle(article);
									termAString = termA[1];
									if(termAString.contains(",")){
										String[] termASplit = termAString.split(",");
										for(int a=1;a<termASplit.length;a++){
											mA.addSynonym(article,termASplit[a],"n.a.");
										}
										
										mA.setMeaning(termASplit[0]);
									}
									else mA.setMeaning(termA[1]);
									mA.setPhonetic(termA[2]);
									mA.setNote(w[3].equals("~")?"":w[3]);		
									mA.setCategory("n.a.");
									
									String[] termB =w[2].split(":");
									article = termB[0].equals("~")?"":termB[0];
									mB.setArticle(article);
									termBString = termB[1];
									if(termBString.contains(",")){
										String[] termBSplit = termBString.split(",");
										for(int a=1;a<termBSplit.length;a++){
											mB.addSynonym(article,termBSplit[a],"n.a.");
										}
										mB.setMeaning(termBSplit[0]);
									}
									else mB.setMeaning(termB[1]);
									mB.setPhonetic(termB[2]);
									mB.setNote(w[4].equals("~")?"":w[4]);
									mB.setCategory("n.a.");
									if(!w[5].equals("~~~")){
										String[] metaA = w[5].split("%");
										for(int m=0;m<metaA.length;m++){
											if(metaA[m]!=null){
												String[] maS = metaA[m].split(":");
												mA.putMetaElement(maS[0],(maS[1]==null||maS[1].equals("~"))?"":maS[1]);
											}
										}
									}
									if(!w[6].equals("~~~")){
										String[] meta = w[6].split("%");
										for(int m=0;m<meta.length;m++){
											if(meta[m]!=null){
												String[] maS = meta[m].split(":");
												mB.putMetaElement(maS[0],(maS[1]==null||maS[1].equals("~"))?"":maS[1]);
											}
										}
									}
									mngsA.add(mA);
									mngsB.add(mB);
									try{
										String[] meaningsA = w[7].split(":");
										Meaning mn;
										for(String s : meaningsA){
											String[] tmpSplit=s.split("%");
											if(!tmpSplit[1].equals("~")){
												mn = new Meaning();
												article = tmpSplit[0].equals("~")?"":tmpSplit[0];
												mn.setArticle(article);
												termAString = tmpSplit[1];
												mn.setCategory("n.a.");
												if(termAString.contains(",")){
													String[] termASplit = termAString.split(",");
													for(int a=1;a<termASplit.length;a++){													
														mA.addSynonym(article,termASplit[a],"n.a.");
													}
													mn.setMeaning(termASplit[0]);
												}
												else{
													mn.setMeaning(termAString);
												}
												if(!w[5].equals("~~~")){
													String[] metaA = w[5].split("%");
													for(int m=0;m<metaA.length;m++){
														if(metaA[m]!=null){
															String[] maS = metaA[m].split(":");
															mn.putMetaElement(maS[0],(maS[1]==null||maS[1].equals("~"))?"":maS[1]);
														}
													}
												}
												mngsA.add(mn);
											}
										}
										String[] meaningsB = w[8].split(":");
										String[] tmpSplit;
										for(String s : meaningsB){
											tmpSplit = s.split("%");
											if(!tmpSplit[1].equals("~")){
												mn = new Meaning();
												mn.setArticle(tmpSplit[0].equals("~")?"":tmpSplit[0]);
												termBString = tmpSplit[1];
												mn.setCategory("n.a.");
												if(termBString.contains(",")){
													String[] termBSplit = termBString.split(",");
													for(int a=1;a<termBSplit.length;a++){
														mB.addSynonym(article,termBSplit[a],"n.a.");
													}
													mn.setMeaning(termBSplit[0]);
												}
												else{
													mn.setMeaning(termBString);
												}
												if(!w[6].equals("~~~")){
													String[] meta = w[6].split("%");
													for(int m=0;m<meta.length;m++){
														if(meta[m]!=null){
															String[] maS = meta[m].split(":");
															mn.putMetaElement(maS[0],(maS[1]==null||maS[1].equals("~"))?"":maS[1]);
														}
													}
												}
												mngsB.add(mn);
											}
										}
									}
									catch(Exception e){
										e.printStackTrace();
									}
									wd = mngsA.firstElement().getMeaning()+" - "+mngsB.firstElement().getMeaning();
									if(!loaded.contains(wd)){
										wordBuffer.add(MAINVOC.makeWord(lection,termA[3],termB[3],mngsA,mngsB));
										loaded.add(wd);
									}
								}
								loadingVoc.setWords(wordBuffer.toArray(new Word[]{}));
								wordBuffer.clear();
								loaded.clear();
							}
							else if(VERBOSE) System.out.println("# "+fileList[r]+ " has no words");
						}
						else{
							if(!voc_split[8].equals("nowords")){
								String[] words_split = voc_split[8].split("#");
								if(VERBOSE) System.out.println("# "+tmpVocName+" has "+words_split.length+" words");
								String termClassA,termClassB,section;
								String[] meaningsA,meaningsB,w,fields,synonyms,t,metas;
								Meaning m;
								Vector<Meaning> mngsA = new Vector<Meaning>(),mngsB= new Vector<Meaning>();
								loader.setVocabulary(loadingVoc.getName(),words_split.length);
								loaderCounter = 0;
								for(int j=0;j<words_split.length;j++){
									loader.setProgress(++loaderCounter);
									mngsA = new Vector<Meaning>();
									mngsB = new Vector<Meaning>();
									w = words_split[j].split(">");
									section = w[0];
									if(w[0].equals("~"))section = LANG.getUnknown();
									termClassA =w[1];
									termClassB =w[2];
									meaningsA =w[3].split(";");
									meaningsB =w[4].split(";");
									m = new Meaning();
									for(String split : meaningsA){
										if(split!=null){
											m = new Meaning();
											fields =split.split(":");
											synonyms = fields[0].split("\\+");
											String term;
											String[] main = synonyms[0].split("%");
											m.setArticle(!main[0].equals("~")?main[0]:"");
											m.setMeaning(main[1]);
											m.setCategory(main[2]);
											for(int i = 1;i<synonyms.length;i++){
												term = synonyms[i];
												if(term!=null&&!term.equals("~~~")){
													t = term.split("%");
													m.addSynonym((!t[0].equals("~")?t[0]:""),t[1],t[2]);
												}	
											}
											m.setPhonetic((fields[1].equals("~")|| fields[1]==null)?"":fields[1]);
											m.setNote((fields[2].equals("~")|| fields[2]==null)?"":fields[2]);
											m.setExampleSentence((fields[3].equals("~")|| fields[3]==null)?"":fields[3]);
											metas = fields[4].split("\\+");
											if(!metas[0].equals("~~~")){
												for(String meta : metas){
													if(meta!=null){
														t = meta.split("%");
														m.putMetaElement(t[0],t[1].equals("~")?"":t[1]);
													}
												}
											}
											mngsA.add(m);
										}
									}
									for(String split : meaningsB){
										if(split!=null){
											m = new Meaning();
											fields =split.split(":");
											synonyms = fields[0].split("\\+");
											String term;
											String[] main = synonyms[0].split("%");
											m.setArticle(!main[0].equals("~")?main[0]:"");
											m.setMeaning(main[1]);
											m.setCategory(main[2]);
											for(int i = 1;i<synonyms.length;i++){
												term = synonyms[i];
												if(term!=null&&!term.equals("~~~")){
													t = term.split("%");
													m.addSynonym((!t[0].equals("~")?t[0]:""),t[1],t[2]);
												}	
											}
											m.setPhonetic((fields[1].equals("~")|| fields[1]==null)?"":fields[1]);
											m.setNote((fields[2].equals("~")|| fields[2]==null)?"":fields[2]);
											m.setExampleSentence((fields[3].equals("~")|| fields[3]==null)?"":fields[3]);
											metas = fields[4].split("\\+");
											if(!metas[0].equals("~~~")){
												for(String meta : metas){
													if(meta!=null){
														t = meta.split("%");
														m.putMetaElement(t[0],t[1].equals("~")?"":t[1]);
													}
												}
											}
											mngsB.add(m);
										}
									}
									wd = mngsA.firstElement().getMeaning()+" - "+mngsB.firstElement().getMeaning();
									if(!loaded.contains(wd)){
										wordBuffer.add(MAINVOC.makeWord(section,termClassA,termClassB,mngsA,mngsB));
										//MAINVOC.addWordToVoc(loadingVoc.getName(), MAINVOC.makeWord(section,termClassA,termClassB,mngsA,mngsB));
										loaded.add(wd);
									}
								}
								MAINVOC.getVocabulary(tmpVocName).setWords(wordBuffer.toArray(new Word[]{}));
								wordBuffer.clear();
								loaded.clear();
							}
							else if(VERBOSE) System.out.println("# "+fileList[r]+ " has no words");
						}
						MAINVOC.getVocabulary(tmpVocName).setChanged(false);
					}
					else if(VERBOSE) System.out.println("#File "+fileList[r]+ " is empty!!!");
				}
			}
			MAINVOC.setStatistics(new Statistics(MAINVOC,SET,loader));
		}
		catch(Exception e){
			e.printStackTrace();
			return false;
		}
		SET.setAutosave(oldAutosave);
		MAINVOC.setLoading(false);
		return true;
    }//load
    
    public UTF8Table getCharTable(){
    	return CHARTABLE;
    }

}//main-class

